package mongoUtil

import (
	"context"
	"fmt"
	"github.com/pkg/errors"
	"go.mongodb.org/mongo-driver/bson"
	"go.mongodb.org/mongo-driver/mongo"
	"go.mongodb.org/mongo-driver/mongo/options"
	"log"
)

func ParseMap(ms map[string]*any, filter *bson.M) *bson.M {
	for s, a := range ms {
		(*filter)[s] = bson.M{
			"$regex":   a,
			"$options": "im",
		}
	}
	return filter
}

func FindOne[T any](ctx context.Context, coll *mongo.Collection, filter any, opts ...*options.FindOneOptions) (*T, error) {
	var result T
	err := coll.FindOne(ctx, filter, opts...).Decode(&result)
	if err != nil {
		return nil, err
	}
	return &result, nil
}

func Find[T any](ctx context.Context, coll *mongo.Collection, filter any, opts ...*options.FindOptions) (int64, []*T, error) {
	resultCount, err := coll.CountDocuments(ctx, filter)
	if err != nil {
		return 0, nil, err
	}
	cursor, err := coll.Find(ctx, filter, opts...)
	var results []*T
	if err != nil {
		return 0, nil, err
	}
	defer cursor.Close(ctx)
	if err = cursor.All(ctx, &results); err != nil {
		return 0, nil, err
	}
	return resultCount, results, err
}
func SelectPage[T any](ctx context.Context, coll *mongo.Collection, filter, skip, limit int64, sort any) (int64, []T, error) {
	resultCount, err := coll.CountDocuments(ctx, filter)
	if err != nil {
		return 0, nil, err
	}
	opts := options.Find().SetSort(sort).SetSkip(skip).SetLimit(limit)
	cursor, err := coll.Find(ctx, filter, opts)
	if err != nil {
		return resultCount, nil, err
	}
	defer cursor.Close(ctx)
	result := make([]T, 0)
	if err := cursor.All(ctx, &result); err != nil {
		return resultCount, nil, err
	}
	return resultCount, result, nil
}

func UpdateOne[T any](ctx context.Context, coll *mongo.Collection, filter any, update *T, opts ...*options.UpdateOptions) (int64, error) {
	updateT := bson.M{"$set": update}
	result, err := coll.UpdateOne(ctx, filter, updateT, opts...)
	if err != nil {
		return 0, err
	}
	if result.MatchedCount == 0 {
		return 0, errors.New("nothing changed")
	}
	return result.MatchedCount, nil
}
func UpdateMany[T any](ctx context.Context, coll *mongo.Collection, filter any, update []T) (int64, error) {
	result, err := coll.UpdateMany(ctx, filter, update, options.Update())
	if err != nil {
		return 0, err
	}
	if result.MatchedCount == 0 {
		return 0, fmt.Errorf("UpdateById result: %s ", "document not found")
	}
	return result.MatchedCount, nil
}
func Delete(ctx context.Context, coll *mongo.Collection, filter any) (int64, error) {
	result, err := coll.DeleteMany(ctx, filter, options.Delete())
	if err != nil {
		return 0, err
	}
	if result.DeletedCount == 0 {
		return 0, fmt.Errorf("DeleteOne result: %s ", "document not found")
	}
	return result.DeletedCount, nil
}

func CreateIndexes(ctx context.Context, coll *mongo.Collection, indexes []mongo.IndexModel) {
	str, err := coll.Indexes().CreateMany(ctx, indexes, options.CreateIndexes())
	if err != nil {
		panic(err)
	}
	fmt.Println(str)
}

func InsertOne[T any](ctx context.Context, coll *mongo.Collection, value *T, opts ...*options.InsertOneOptions) error {
	_, err := coll.InsertOne(ctx, value, opts...)
	if err != nil {
		return err
	}
	return nil
}
func ReplaceOne[T any](ctx context.Context, coll *mongo.Collection, filter any, value any, opts ...*options.ReplaceOptions) error {
	_, err := coll.ReplaceOne(ctx, filter, value, opts...)
	if err != nil {
		return err
	}
	return nil
}
func InsertMany[T any](ctx context.Context, coll *mongo.Collection, value []*T, opts ...*options.InsertManyOptions) error {
	anies := make([]any, len(value))
	for i, r := range value {
		anies[i] = r
	}
	_, err := coll.InsertMany(ctx, anies, opts...)
	return err

}

// TransactionSomeThing 仅仅多节点使用
func TransactionSomeThing[T any](ctx context.Context, client *mongo.Client, coll *mongo.Collection, filter any, value any, handle TransactionHandle) {
	if err := client.UseSession(ctx, func(sessionContext mongo.SessionContext) error {
		//开启事务
		if err := sessionContext.StartTransaction(); err != nil {
			return err
		}
		//插入数据
		err := handle(sessionContext, coll, filter, value)
		if err != nil {
			return err
		}
		//提交事务
		return sessionContext.CommitTransaction(context.Background())
	}); err != nil {
		log.Println("insert failed, err:%s", err.Error())

	}
}

type TransactionHandle func(sessionContext mongo.SessionContext, coll *mongo.Collection, filter any, value any) error
